using Gee;

public errordomain HttpProtocolError {
    InvalidProtocol,
    InvalidMethod,
    InvalidProtocolVersion
}
public enum HttpMethods {
    GET,
    POST,
    PUT,
    DELETE,
    HEAD,
    OPTIONS
}

public class HttpRequest {
    public weak HttpContext context {get; private set;}
    public string path {get; private set;}
    public float version {get; private set;}
    public HttpMethods method {get; private set;}
    public Map<string, Header.HttpHeader> headers {get; private set;}
    public static async HttpRequest parse(HttpContext context) throws IOError, HttpProtocolError {
        var request = new HttpRequest();
        request.context = context;
        yield request.read_from_socket();
        return request;
    }
    public uint64 content_length {
        get {
            var header = this.headers[Header.ContentLength.KEY];
            return (header as Header.ContentLength).value;
        }
    }
    private async void read_from_socket() throws IOError, HttpProtocolError {
        var reader = new DataInputStream(this.context.connection.input_stream);
        var http_header = yield reader.read_line_utf8_async();
        var segments = http_header.split(" ");
        if (segments.length != 3) throw new HttpProtocolError.InvalidProtocol("Invalid http header");
        this.path = segments[1];
        if (!get_http_methods().has_key(segments[0])) throw new HttpProtocolError.InvalidMethod(@"Invalid http method $(segments[0])");
        this.method = get_http_methods()[segments[0]];
        var versionString = segments[2].split("/")[1];
        versionString = versionString.substring(0, versionString.length - 1);
        float version = 0f;
        if (!float.try_parse(versionString, out version)) throw new HttpProtocolError.InvalidProtocolVersion(@"Invalid http version: $(versionString)");
        this.headers = new HashMap<string, Header.HttpHeader>();
        string line;
        while ((line = yield reader.read_line_utf8_async()) != "\r") {
            var keyValues = line.replace(" ", "").split(":");
            keyValues[1] = keyValues[1].substring(0, keyValues[1].length);
            var key = keyValues[0];
            Header.HttpHeader header = new Header.Generic();
            if (this.context.server.headerHandlers.has_key(key)) {
                var type = this.context.server.headerHandlers[key];
                header = Object.new(type) as Header.HttpHeader;
            }
            header.parse_value(keyValues[1]);
            this.headers.set(key, header);
        }
    }
}

private Map<string, HttpMethods> httpMethods = null;
public Map<string, HttpMethods> get_http_methods() {
    if (httpMethods == null) {
        httpMethods = new HashMap<string, HttpMethods>();
        httpMethods["GET"] = HttpMethods.GET;
        httpMethods["PUT"] = HttpMethods.PUT;
        httpMethods["POST"] = HttpMethods.POST;
        httpMethods["DELETE"] = HttpMethods.DELETE;
        httpMethods["HEAD"] = HttpMethods.HEAD;
        httpMethods["OPTIONS"] = HttpMethods.OPTIONS;
    }
    return httpMethods;
}